package com.zhou.base.resilience4j.test.util;

import com.zhou.base.resilience4j.test.entity.SystemLog;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import io.vavr.CheckedFunction0;
import io.vavr.control.Try;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Supplier;

@Slf4j
@Component
public class CircuitBreakerServiceImpl {

    @Autowired
    private CircuitBreakerRegistry circuitBreakerRegistry;

    @Autowired
    private RemoteServiceConnector remoteServiceConnector;

    public List<SystemLog> circuitBreakerWithDisable() throws Throwable {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA");
        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreaker);
        return circuitBreaker.executeCheckedSupplier(remoteServiceConnector::process);
    }

    public List<SystemLog> circuitBreakerWithEnableNotAOP() {
        // 通过注册器获取熔断器的实例
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA");
        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreaker);
        // 使用熔断器包装连接器的方法
        CheckedFunction0<List<SystemLog>> checkedSupplier = CircuitBreaker.
                decorateCheckedSupplier(circuitBreaker, remoteServiceConnector::process);
        // 使用Try.of().recover()调用并进行降级处理
        Try<List<SystemLog>> result = Try.of(checkedSupplier).
                recover(CallNotPermittedException.class, throwable -> {
                    log.info("熔断器已经打开，拒绝访问被保护方法~");
                    CircuitBreakerUtil
                            .getCircuitBreakerStatus("熔断器打开中:", circuitBreaker);
                    List<SystemLog> users = new ArrayList();
                    return users;
                })
                .recover(throwable -> {
                    log.info(throwable.getLocalizedMessage() + ",方法被降级了~~");
                    CircuitBreakerUtil
                            .getCircuitBreakerStatus("降级方法中:", circuitBreaker);
                    List<SystemLog> users = new ArrayList();
                    return users;
                });
        CircuitBreakerUtil.getCircuitBreakerStatus("执行结束后：", circuitBreaker);
        return result.get();
    }

    public void circuitBreakerWithAnnotation() {
        remoteServiceConnector.processNew();
    }

/*    @Autowired
    private TimeLimiter timeLimiter;*/

    @Autowired
    private TimeLimiterRegistry timeLimiterRegistry;

    public List<SystemLog> circuitBreakerTimeLimiter() {
        // 通过注册器获取熔断器的实例
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA");
        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreaker);
        // 创建单线程的线程池
        TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter("backendA");
        ExecutorService pool = Executors.newSingleThreadExecutor();
        //将被保护方法包装为能够返回Future的supplier函数
        Supplier<Future<List<SystemLog>>> futureSupplier = () -> pool.submit(remoteServiceConnector::process1);
        // 先用限时器包装，再用熔断器包装
        Callable<List<SystemLog>> restrictedCall = TimeLimiter.decorateFutureSupplier(timeLimiter, futureSupplier);
        Callable<List<SystemLog>> chainedCallable = CircuitBreaker.decorateCallable(circuitBreaker, restrictedCall);
        // 使用Try.of().recover()调用并进行降级处理
        Try<List<SystemLog>> result = Try.of(chainedCallable::call)
                .recover(CallNotPermittedException.class, throwable -> {
                    log.info("熔断器已经打开，拒绝访问被保护方法~");
                    CircuitBreakerUtil.getCircuitBreakerStatus("熔断器打开中", circuitBreaker);
                    List<SystemLog> users = new ArrayList();
                    return users;
                })
                .recover(throwable -> {
                    log.info(throwable.getLocalizedMessage() + ",方法被降级了~~");
                    CircuitBreakerUtil.getCircuitBreakerStatus("降级方法中:", circuitBreaker);
                    List<SystemLog> users = new ArrayList();
                    return users;
                });
        CircuitBreakerUtil.getCircuitBreakerStatus("执行结束后：", circuitBreaker);
        return result.get();
    }

    @Autowired
    private RetryRegistry retryRegistry;

    public List<SystemLog> circuitBreakerRetryNotAOP() {
        // 通过注册器获取熔断器的实例
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("backendA");
        // 通过注册器获取重试组件实例
        Retry retry = retryRegistry.retry("backendA");
        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreaker);
        // 先用重试组件包装，再用熔断器包装
        CheckedFunction0<List<SystemLog>> checkedSupplier = Retry.decorateCheckedSupplier(retry, remoteServiceConnector::processRetry2);
        CheckedFunction0<List<SystemLog>> chainedSupplier = CircuitBreaker.decorateCheckedSupplier(circuitBreaker, checkedSupplier);
        // 使用Try.of().recover()调用并进行降级处理
        Try<List<SystemLog>> result = Try.of(chainedSupplier).
                recover(CallNotPermittedException.class, throwable -> {
                    log.info("已经被熔断，停止重试");
                    return new ArrayList<>();
                })
                .recover(throwable -> {
                    log.info("重试失败: " + throwable.getLocalizedMessage());
                    return new ArrayList<>();
                });
        RetryUtil.getRetryStatus("执行结束: ", retry);
        CircuitBreakerUtil.getCircuitBreakerStatus("执行结束：", circuitBreaker);
        return result.get();
    }

    public List<SystemLog> circuitBreakerRetryAOP() throws TimeoutException, InterruptedException {
        List<SystemLog> result = remoteServiceConnector.processRetry();
        RetryUtil.getRetryStatus("执行结束：", retryRegistry.retry("backendA"));
        CircuitBreakerUtil
                .getCircuitBreakerStatus("执行结束：", circuitBreakerRegistry.circuitBreaker("backendA"));
        return result;
    }
}